"""
---------------------------------------------------------------------
- File: dependency.py
- Date: 2025/11/13
- Author: yangzhide
- Email: xxzjyzd@126.com
- Description: 项目依赖项
---------------------------------------------------------------------
"""

from datetime import datetime, timezone, timedelta
from typing import AsyncGenerator, Type, Callable, Coroutine, Any, get_args

import jwt
from fastapi import Depends, HTTPException, Request
from jwt.exceptions import InvalidTokenError
from sqlalchemy.orm import selectinload
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

from app.core.config import settings
from app.core.security import oauth2_scheme
from app.db.session import AsyncSessionLocal
from app.models.system import Users
from app.schemas.enums import UserStatusEnum
from app.schemas.system.login import AuthenticatedUser
from app.service.service_crud import CRUDBase


async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    """
    异步数据库会话依赖项
    """
    async with AsyncSessionLocal() as session:
        try:
            yield session
        except Exception as e:
            await session.rollback()
            # 直接跑出，由全局异常处理器处理
            raise e
        finally:
            await session.close()


def get_service_dependency(curd_class: Type[CRUDBase]) -> Callable[..., Coroutine[Any, Any, CRUDBase]]:
    """
    CRUD依赖项,该函数在被调用时会实例化并返回一个CRUD对象
    """
    # 使用高级特效推断CRUD中的ModelType

    try:
        base = curd_class.__orig_bases__[0]  # 得到如： CRUDBase[Glass, GlassCreate, GlassUpdate]
        generic_args = get_args(base)  # 得到如： (Glass, GlassCreate, GlassUpdate)
        # 拿到model类型
        model = generic_args[0]
    except (AttributeError, IndexError):
        raise TypeError(f"无法从{curd_class}自动推断出Model类型, 请确保它继承自CURDBase并提供了泛型参数")

    async def _get_crud_dependency(session: AsyncSession = Depends(get_async_session)) -> CRUDBase:
        """
        返回一个可以被FastAPI Depends依赖项
        """
        return curd_class(session=session, model=model)

    return _get_crud_dependency


async def create_access_token(data: dict):
    """
    创建JWT token
    """
    to_encode = data.copy()
    expire = datetime.now(timezone.utc) + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
    return encoded_jwt


async def get_current_user(
    request: Request, token: str = Depends(oauth2_scheme), session: AsyncSession = Depends(get_async_session)
):
    """
    获取当前用户依赖项
    """
    # 凭证异常
    credentials_exception = HTTPException(
        status_code=401,
        detail="凭证认证失败",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        # jwt解码, 得到加密时的payload
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        user_no: str = payload.get("user_no")
        if user_no is None:
            raise credentials_exception
        statement = select(Users).options(selectinload(Users.roles)).where(Users.user_no == user_no)
        result = await session.exec(statement)
        user: Users | None = result.first()
        if user is None:
            raise credentials_exception
        if user.status == UserStatusEnum.DISABLED.value:
            raise credentials_exception

        # 添加用户对象附加到request.state,供后续日志存储使用
        request.state.auth_user = AuthenticatedUser(id=user.id, user_no=user.user_no, user_name=user.user_name)
        return user
    except InvalidTokenError:
        raise credentials_exception
